﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;


namespace Effects.Testing
{

    // Struct representing some number of adjecant samples by storing values of minimum and maximum samples
    public struct MinMaxSamplesPerPixel
    {
        // minimum value 
        private double min;
        public double Min
        {
            get { return min; }
            set { min = value; }
        }

        // maximum value
        private double max;
        public double Max
        {
            get { return max; }
            set { max = value; }
        }

        public MinMaxSamplesPerPixel(double min, double max)
        {
            this.min = min;
            this.max = max;
        }
    }

    // User control used to show waveform of given wave
    // Firt it prepares wave by creating array of MinMaxSamplesPerPixel - structs, that will be later used for drawing image
    public class ShowWave:UserControl
    {
        // given instace of Wave which is to be shown
        protected Wave.Wave wav;
        public Wave.Wave Wav
        {
            get { return wav; }
            set
            {
                wav = value;
                Prepare();
                Refresh();
            }
        }

        // how many samples will be represented by one MinMaxSamplesPerPixel - struct
        private int samplesPerStruct=100;

        // array of MinMaxSamples...
        private MinMaxSamplesPerPixel[] values;

        // interval that is shown 
        protected int begin;
        protected int end;


        protected HScrollBar scBar;
        public HScrollBar ScrollBar
        {
            get { return scBar; }
            set
            {
                scBar = value;
                if (usingSamples)
                    ScrollBarSamples();
                else
                    ScrollBarStructs();

                scBar.Scroll += new ScrollEventHandler(this.ScrollBarValue_Changed);
            }
        }

        public ShowWave(Wave.Wave wav)
        {
            InitializeComponents();
            
            this.wav = wav;
            Prepare();
            penColor = Color.Blue;
        }

        private void InitializeComponents()
        {
            scBar = new HScrollBar();
            scBar.Location = new Point(0, 100);
            
        }

        // prepares given Wave
        protected void Prepare()
        {
            /*int arrayLenght = Convert.ToInt32( Math.Ceiling( ((double)wav.SampleLength) / samplesPerStruct ) );
            values = new MinMaxSamplesPerPixel[arrayLenght];
            
            int valuesIndex = 0;
            int count = 0;
            int counter=0;

            double max = double.MinValue;
            double min = double.MaxValue;

            foreach (double sample in wav.GetSampleDoubleEnumerator)
            {
                max = Math.Max(max, sample);
                min = Math.Min(min, sample);
                count++;
                counter++;

                if (count == samplesPerStruct)
                {
                    values[valuesIndex] = new MinMaxSamplesPerPixel(min, max);
                    min = double.MaxValue;
                    max = double.MinValue;

                    count = 0;
                    valuesIndex++;
                }
           }

            // creating last struct
            if (valuesIndex < arrayLenght)
            {
                values[valuesIndex] = new MinMaxSamplesPerPixel(min, max);
            }

            begin = 0;
            end = values.Length;
            usingSamples = false;*/
        }

        protected Color penColor;
        public Color PenColor
        {
            get { return penColor; }
            set { penColor = value; }
        }

        private Bitmap image;


        private void Render()
        {
            if (usingSamples)
                RenderSamples();
            else
                RenderStructs();

            RenderImage();
        }

        // array used to store output of render
        private MinMaxSamplesPerPixel[] imageMinMax;

        private void RenderImage()
        {
            Bitmap newImage = new Bitmap(Width, Height);
            Graphics g = Graphics.FromImage(newImage);

            int halfHeight = this.Height / 2;
            g.FillRectangle(new SolidBrush(this.BackColor), 0, 0, Width, Height);

            Pen p = new Pen(penColor);

            for (int x = 0; x < Width; x++)
            {
                int y1 = Convert.ToInt32(halfHeight - halfHeight * imageMinMax[x].Max);
                int y2 = Convert.ToInt32(halfHeight - halfHeight * imageMinMax[x].Min);
                if (y1 == y2)
                    g.DrawRectangle(p, x, y1, 1, 1);
                else
                    g.DrawLine(p, x, y1, x, y2);
            }

            g.DrawLine(Pens.Black, 0, halfHeight, Width, halfHeight);
            image = newImage;
        }

        private void RenderSamples()
        {
            /*Bitmap newBackground = new Bitmap(this.Width, this.Height);
            Graphics g = Graphics.FromImage(newBackground);

            int halfHeight = this.Height / 2;
            g.FillRectangle(new SolidBrush(this.BackColor), 0, 0, Width, Height);

            Pen p = new Pen(penColor); */

            MinMaxSamplesPerPixel[] newArray = new MinMaxSamplesPerPixel[this.Width];

            int samplesPerPixel = (end - begin) / Width;
            int sampleIndex = begin;

            for (int x = 0; x < this.Width; x++)
            {
                double min = double.MaxValue;
                double max = double.MinValue;
                for (int i = 0; i < samplesPerPixel; i++)
                {
                    min=Math.Min(min,wav.getSample(1,sampleIndex));
                    max=Math.Max(max,wav.getSample(1,sampleIndex));

                    sampleIndex++;
                }
                /*
                int y1=Convert.ToInt32(halfHeight - halfHeight * max);
                int y2=Convert.ToInt32(halfHeight - halfHeight * min);
                if (y1 == y2)
                    g.DrawRectangle(p, x, y1, 1, 1);
                else
                    g.DrawLine(p, x, y1, x, y2);*/
                newArray[x] = new MinMaxSamplesPerPixel(min, max);

            }


            //g.DrawLine(Pens.Black, 0, halfHeight, Width, halfHeight);
            //image = newBackground;

            imageMinMax = newArray;
        }
        
        private void RenderStructs()
        {
            /*Bitmap newBackground = new Bitmap(this.Width, this.Height);
            Graphics g = Graphics.FromImage(newBackground);
            
            int halfHeight=this.Height/2;
            g.FillRectangle(new SolidBrush(this.BackColor), 0, 0, Width, Height);

            Pen p = new Pen(penColor);*/

            MinMaxSamplesPerPixel[] newArray = new MinMaxSamplesPerPixel[Width];

            //int structsPerPixel=values.Length/this.Width;
            int structsPerPixel = (end - begin) / this.Width;

            int valuesIndex = begin;

            for (int x = 0; x < this.Width-1; x++)
            {
                double min = double.MaxValue;
                double max = double.MinValue;

                for (int i = 0; i < structsPerPixel; i++)
                {
                    min = Math.Min(min, values[valuesIndex].Min);
                    max = Math.Max(max, values[valuesIndex].Max);
                    valuesIndex++;
                }
                
                //g.DrawLine(p,x,Convert.ToInt32( halfHeight- halfHeight*max), x, Convert.ToInt32( halfHeight-halfHeight*min));
                newArray[x] = new MinMaxSamplesPerPixel(min, max);
            }

            //g.DrawLine(Pens.Black, 0, halfHeight, Width, halfHeight);
            //image=newBackground;

            imageMinMax = newArray;
        }

        public void MoveTo(int newBegin, int newEnd)
        {
            int interval = newEnd - newBegin;
            if (interval != this.end - this.begin)
                throw new ArgumentException("Given interval does not fit current zoom.");

            begin = newBegin;
            end = newEnd;

            Render();
            Refresh();

            //if(newBegin > begin && newBegin < end)
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            //base.OnPaint(e);
            Graphics g = e.Graphics;
            try
            {
                g.DrawImage(image, 0, 0);
            }
            catch (ArgumentNullException ex)
            {
                Render();
                Refresh();
            }
        }

        protected bool usingSamples;

        protected void ZoomIn(double X)
        {
            if (usingSamples)
            {
                ZoomInSamples(X);
            }
            else
            {
                int newInterval = (end - begin) / 2;

                if (((double)Width / newInterval) < 1)
                {
                    int newInterval2 = newInterval / 2;

                    int newMiddle = begin + Convert.ToInt32(X * (end - begin));

                    if (newMiddle < newInterval2)
                        newMiddle = newInterval2;

                    if (newMiddle > values.Length - newInterval2)
                        newMiddle = values.Length - newInterval2;

                    begin = newMiddle - newInterval2;
                    end = newMiddle + newInterval2;

                    Render();
                    Refresh();
                }
                else
                {
                    // Zoom too close, time to use real samples
                    usingSamples = true;

                    // count begin, end
                    int structsPerPixel = (end - begin) / this.Width;
                    begin = begin * structsPerPixel * samplesPerStruct;
                    end = end * structsPerPixel * samplesPerStruct;

                    ZoomIn(X);
                }
            }
        }

        protected void ZoomInSamples(double X)
        {
            int newInterval = (end - begin) / 2;

            if (((double)Width / newInterval) < 1)
            {
                int newInterval2 = newInterval / 2;

                int newMiddle = begin + Convert.ToInt32(X * (end - begin));

                if (newMiddle < newInterval2)
                    newMiddle = newInterval2;

                if (newMiddle > wav.SampleLength - newInterval2)
                    newMiddle = Convert.ToInt32(wav.SampleLength - newInterval2);

                begin = newMiddle - newInterval2;
                end = newMiddle + newInterval2;

                Render();
                Refresh();
            }
        }

        protected void ZoomOut()
        {
            if (usingSamples)
            {
                ZoomOutSamples();
            }
            else
            {
                int middle = (begin + end) / 2;
                int interval = end - begin;

                if (2 * interval <= values.Length)
                {
                    begin = middle - interval;
                    if (begin < 0)
                    {
                        begin = 0;
                        end = 2 * interval;
                    }
                    else
                    {
                        end = middle + interval;
                        if (end > values.Length)
                        {
                            end = values.Length;
                            begin = end - 2 * interval;
                        }
                    }

                    Render();
                    Refresh();
                }
            }
        }

        protected void ZoomOutSamples()
        {
            int middle = (begin + end) / 2;
            int interval = end - begin;

            if ((interval / samplesPerStruct) / Width < 1)
            {
                begin = middle - interval;
                if (begin < 0)
                {
                    begin = 0;
                    end = 2 * interval;
                }
                else
                {
                    end = middle + interval;
                    if (end > wav.SampleLength)
                    {
                        end = Convert.ToInt32(wav.SampleLength);
                        begin = end - 2 * interval;
                    }
                }

                Render();
                Refresh();
            }
            else
            {
                begin = begin / samplesPerStruct;
                end = end / samplesPerStruct;
                usingSamples = false;
                ZoomOut();
            }
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            //base.OnMouseWheel(e);
            double where = (double)e.X / Width;
            if (e.Delta > 0)
                ZoomIn(where);
            else
                ZoomOut();
            
            // count ScrollBar
            if (usingSamples)
                ScrollBarSamples();
            else
                ScrollBarStructs();
        }

        protected void ScrollBarStructs()
        {
            int minimum = 0;
            int maximum = 100 * values.Length / (end - begin)  - 99;

            int current = begin * maximum / values.Length;


            scBar.Minimum = minimum;
            scBar.Maximum = maximum;
            scBar.Value = current;
        }

        protected void ScrollBarSamples()
        {
            int minimum=0;
            int maximum= Convert.ToInt32(100*wav.SampleLength/(end-begin) - 99);

            int current = Convert.ToInt32(begin * ( (double) maximum / wav.SampleLength));
          
            scBar.Minimum = minimum;
            scBar.Maximum = maximum;
            scBar.Value = current;
        }

        protected override void OnClick(EventArgs e)
        {
            base.OnClick(e);
            Render();
            Refresh();
        }

        private void ScrollBarValue_Changed(object sender, ScrollEventArgs e)
        {
            int interval = end - begin;
            int newBegin=e.NewValue * interval / 100;
            MoveTo(newBegin, newBegin + interval);
        }
    }
}
